package Game;

import java.lang.reflect.InvocationTargetException;
import java.util.*;

import org.newdawn.slick.Graphics;
import org.newdawn.slick.Image;
import org.newdawn.slick.SlickException;
import org.newdawn.slick.geom.Vector2f;

import Game.MapBlock.EMapBlockType;
import Game.Objects.GOPlayer;
import Game.Objects.ObjectManager;
import Game.Spawners.SpawnerBase;
import GameMath.GSize;
import Screens.Manager.Screen;
import Tools.Metrics;
import Tools.ResourceManager;
import Tools.Lighting.LightManager;

/** Level class
 * @author Aikidos
 */
public class Level {
	
	/** Level object manager */
	private ObjectManager _objectManager;
	
	/** Light manager */
	private LightManager _lightManager;
	
	/** Map */
	private LevelMap _levelMap = new LevelMap();
	
	/** Game screen */
	private Screen _gameScreen;
	
	/** Level floor */
	private int _floor;
	
	/** Is underground town? */
	private boolean _isUndergroundTown;
	
	/** @param gameScreen - Game screen*/
	public Level(Screen gameScreen) {
		_gameScreen = gameScreen;
	}
	
	/** Returns object manager
	 * @return Object manager
	 */
	public ObjectManager getObjectManager() {
		return _objectManager;
	}
	
	/** Returns light manager
	 * @return Light manager
	 */
	public LightManager getLightManager() {
		return _lightManager;
	}
	
	/** Returns level screen
	 * @return Level screen (game screen)
	 */
	public Screen getScreen() {
		return _gameScreen;
	}
	
	/** Is underground town? */
	public boolean isUndergroundTown() {
		return _isUndergroundTown;
	}
	
	/** Returns floor
	 * @return Floor
	 */
	public int getFloor() {
		return _floor;
	}
	
	/** Map block size */
	public static int BLOCK_SIZE = 24;
	
	/** Returns level map
	 * @return Level map
	 */
	public LevelMap getMap() {
		return _levelMap;
	}
	
	/** Initialize level */
	public void init() throws SlickException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException, ClassNotFoundException {
		// Initialize lighting manager
		_lightManager = new LightManager();
		_lightManager.setAutoRecalc(false);
		
		// Initialize object manager
		_objectManager = new ObjectManager();
		_objectManager.init(this);
	}

	/** Create level
	 * @param floor - Floor
	 */
	public void create(int floor, List<SpawnerBase> lstSpawners) throws SlickException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException, ClassNotFoundException {
		_floor = floor;
		
		// Create map
		GSize mapSize = new GSize(100, 100);
		_levelMap.createEmptyMap(mapSize);

		Random rnd = GameHelper.getRandom();
		
		// Is underground town? 
		//_isUndergroundTown = (rnd.nextInt(100) <= 10) ? true : false;
		_isUndergroundTown = true;
		
		// Set number of rooms
		_levelMap.setNumOfRooms(_isUndergroundTown ? 15 :
				rnd.nextInt(14) + 2);
		_levelMap.generation();
		
		// Delete all objects
		_objectManager.deleteAllObjects();
		
		// Delete all lights
		_lightManager.getLights().clear();
		
		// Initialize light map and create solid map for light manager
		_lightManager.initLightMap(mapSize);
		boolean[][] solidMap = new boolean[mapSize.width][mapSize.height];
		for (int i = 0; i < mapSize.width; i++)
			for (int j = 0; j < mapSize.height; j++) {
				if (_levelMap.getBlocks()[i][j].Type == EMapBlockType.Wall) solidMap[i][j] = true;
			}
		
		_lightManager.setSolidMap(solidMap);
		
		// Spawn
		for (SpawnerBase sp : lstSpawners) {
			sp.spawn(this);
		}
		
		// Test: disable light and open map
//		_lightManager.disable();
//		_levelMap.openMap();
		
		// Update fog of war
		updateOpenedMap();
		
		_lightManager.calcLighting();
	}

	/** Update level
	 * @param delta - Time delta
	 */
	public void update(int delta) throws SlickException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException, ClassNotFoundException {
		_objectManager.update(delta);
		_lightManager.update(delta);
	}

	/** Draw level 
	 * @param g - Slick2d graphics
	 */
	public void draw(Graphics g) {
        // Draw map
        drawMap(g);

        // Draw objects
        _objectManager.draw(g);
	}

	/** Level step 
	 * @throws SlickException */
	public void step() throws SlickException {
		// Step objects
		_objectManager.step();

        // Update opened map
        updateOpenedMap();
        
        // Calculate lighting
        _lightManager.calcLighting();
	}
	
    /** Update fog of war */
    public void updateOpenedMap() {
    	GSize mapSize = _levelMap.getSize();
    	
    	for (int i = 0; i < mapSize.width; i++) {
            for (int j = 0; j < mapSize.height; j++) {
				if ((_levelMap.getBlocks()[i][j].Type != EMapBlockType.None)
						&& !_levelMap.getBlocks()[i][j].IsOpen) {
					Vector2f blockPos = new Vector2f(
							(i * BLOCK_SIZE) + (BLOCK_SIZE / 2), 
							(j * BLOCK_SIZE) + (BLOCK_SIZE / 2));

					// Get player
					GOPlayer player = _objectManager.getPlayer();

					float dist = Metrics.getDistance(player.Position, blockPos);
					if (dist <= (player.OpenMapRange * Level.BLOCK_SIZE)) {
						_levelMap.getBlocks()[i][j].IsOpen = true;
					}
				}
            }
        }
    }
	
	/** Draw level map
	 * @param g - Slick2d graphics
	 */
	private void drawMap(Graphics g) {
		GSize mapSize = _levelMap.getSize();
		
		for (int i = 0; i < mapSize.width; i++) {
			for (int j = 0; j < mapSize.height; j++) {
				// Get draw position
				Vector2f pos = new Vector2f(i * BLOCK_SIZE, j * BLOCK_SIZE);
				pos.sub(_objectManager.getCamera().getDrawOffset());

				// Get sprite
				Image sprite = null;

				switch (_levelMap.getBlocks()[i][j].Type) {
				case Floor:
					sprite = (Image) ResourceManager.getPool().get("gfx_floor");
					break;
				case Wall:
					sprite = (Image) ResourceManager.getPool().get("gfx_wall");
					break;
				case None:
					break;
				default:
					break;
				}

				// Draw sprite
				if (sprite != null) {
					if (_levelMap.getBlocks()[i][j].IsOpen) {
						float alpha = _lightManager.getLightMap().getMap()[i][j].getIllumination();
						alpha = (alpha <= 0.2f) ? 0.2f : alpha;
						sprite.setAlpha(alpha);
						g.drawImage(sprite, (int)pos.x, (int)pos.y);
					}
				}
			}
		}
	}
	
}
